package com.ut.animator;

import com.ut.animator.curve.AbTimelineCurve;
import com.ut.animator.curve.IEvaluator;
import com.ut.animator.property.Property;
import com.ut.animator.property.ReflectiveProperty;
import com.ut.richpath.util.JsonUtil;
import com.ut.richpath.util.LogUtil;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class ObjectAnimator<T> extends AnimatorValue{

    private static final boolean DBG = false;

    private WeakReference<Object> mTarget;

    private String mPropertyName;

    private Property mProperty;

    private AbTimelineCurve<T> timelineCurve;

    private T startValue, endValue;

    private boolean mInitialized = false;

    private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }

    private <T> ObjectAnimator(T target, Property<T, ?> property) {
        setTarget(target);
        setProperty(property);
    }

    public void setPropertyName(String propertyName) {
        mPropertyName = propertyName;
    }

    public String getPropertyName() {
        String propertyName = null;
        if (mPropertyName != null) {
            propertyName = mPropertyName;
        } else if (mProperty != null) {
            propertyName = mProperty.getName();
        }
        return propertyName;
    }

    public void setProperty(Property property) {
        if (mProperty != null) {
            mPropertyName = property.getName();
        }
        mProperty = property;
        mInitialized = false;
    }


    private static  <T,V> void initProperty(ObjectAnimator anim, Class<T> target, Class<V> valueType, String propertyName){
        anim.mProperty = new ReflectiveProperty<T, V>(target, valueType, propertyName);
    }

    public static ObjectAnimator ofInt(Object target, String propertyName, int startValue, int endValue) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        initProperty(anim, target.getClass(), Integer.class, propertyName);
        anim.setIntValues(startValue, endValue);
        return anim;
    }

    public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int startValue, int endValue) {
        ObjectAnimator anim = new ObjectAnimator(target, property);
        anim.setIntValues(startValue, endValue);
        return anim;
    }

    public static <T> ObjectAnimator ofArgb(Object target, String propertyName, ArgbEvaluator argbEvaluator, int startValue, int endValue) {
        ObjectAnimator animator = ofInt(target, propertyName, startValue, endValue);
        initProperty(animator, target.getClass(), Integer.class, propertyName);
        if (argbEvaluator!=null){
            animator.timelineCurve = argbEvaluator;
            argbEvaluator.setEvaluator(new IEvaluator<Integer>() {
                @Override
                public void onChange(Integer data) {
                    LogUtil.i("animator on change: %s", JsonUtil.toJson(data));
                    if (animator.mProperty!=null){
                        animator.mProperty.set(target, data);
                    }
                }
            });
        }
        animator.setCurve(argbEvaluator);
        return animator;
    }

    public static <T> ObjectAnimator ofArgb(T target, Property<T, Integer> property, ArgbEvaluator argbEvaluator, int startValue, int endValue) {
        ObjectAnimator animator = ofInt(target, property, startValue, endValue);
        if (argbEvaluator!=null){
            animator.timelineCurve = argbEvaluator;
            argbEvaluator.setEvaluator(new IEvaluator<Integer>() {
                @Override
                public void onChange(Integer data) {
                    LogUtil.i("animator on change: %s", JsonUtil.toJson(data));
                    property.set(target, data);
                }
            });
        }
        animator.setCurve(argbEvaluator);
        return animator;
    }

    public static ObjectAnimator ofFloat(Object target, String propertyName, float startValue, float endValue) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        initProperty(anim, target.getClass(), Float.class, propertyName);
        anim.setFloatValues(startValue, endValue);
        return anim;
    }

    public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, float startValue, float endValue) {
        ObjectAnimator anim = new ObjectAnimator(target, property);
        anim.setFloatValues(startValue, endValue);
        return anim;
    }

    public static <T> ObjectAnimator ofObject(Object target, String propertyName, AbTimelineCurve<T> timelineCurve, Object startValue, Object endValue) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        initProperty(anim, target.getClass(), Object.class, propertyName);
        anim.setObjectValues(startValue, endValue);
        if (timelineCurve!=null){
            anim.timelineCurve = timelineCurve;
            timelineCurve.setEvaluator(new IEvaluator<T>() {
                @Override
                public void onChange(T data) {
                    LogUtil.i("animator on change: %s", JsonUtil.toJson(data));
                    if (anim.mProperty!=null){
                        anim.mProperty.set(target, data);
                    }
                }
            });
        }
        anim.setCurve(timelineCurve);
        return anim;
    }

    public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property, AbTimelineCurve<V> timelineCurve, V startValue, V endValue) {
        ObjectAnimator anim = new ObjectAnimator(target, property);
        anim.setObjectValues(startValue, endValue);
        if (timelineCurve!=null){
            anim.timelineCurve = timelineCurve;
            timelineCurve.setEvaluator(new IEvaluator<V>() {
                @Override
                public void onChange(V data) {
                    LogUtil.i("animator on change: %s", JsonUtil.toJson(data));
                    if (anim.mProperty!=null){
                        anim.mProperty.set(target, data);
                    }
                }
            });
        }
        anim.setCurve(timelineCurve);
        return anim;
    }

    public void setIntValues(int startValue, int endValue) {
        initValues(startValue, endValue);
    }

    public void setFloatValues(float startValue,float endValue) {
        initValues(startValue, endValue);
    }

    public void setObjectValues(Object startValue, Object endValue) {
        initValues(startValue, endValue);
    }

    private boolean hasSameTargetAndProperties(Animator anim) {
        if (anim instanceof ObjectAnimator) {
            String theirPropertyName = ((ObjectAnimator) anim).getPropertyName();
            if (((ObjectAnimator) anim).getTarget() == getTarget()) {

                return theirPropertyName!=null && theirPropertyName.equalsIgnoreCase(getPropertyName());
            }
        }
        return false;
    }

    @Override
    public void setDuration(long duration) {
        super.setDuration(duration);
    }

    public Object getTarget() {
        return mTarget == null ? null : mTarget.get();
    }

    public void setTarget(Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            if (isRunning()) {
                cancel();
            }
            mTarget = target == null ? null : new WeakReference<Object>(target);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }

    private void initValues(Object startValue, Object endValue){
        final Object target = getTarget();
        this.startValue = (T)startValue;
        this.endValue = (T)endValue;
        if (timelineCurve!=null)timelineCurve.setStartValue(this.startValue);
        if (timelineCurve!=null)timelineCurve.setEndValue(this.endValue);
        boolean isInteger = mProperty.getType() == Integer.class;
        boolean isFloat = mProperty.getType() == Float.class;
        setValueUpdateListener(new ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                if (timelineCurve==null && mProperty!=null && target!=null){
                    if (isInteger){
//                        LogUtil.i("onUpdate: %f * (%d - %d) = %f", v, endValue, startValue, (v*((Integer)endValue-(Integer) startValue)));
                        mProperty.set(target, (int)(v*((Integer)endValue-(Integer) startValue)));
                    }else if (isFloat){
//                        LogUtil.i("onUpdate: %f * (%f - %f) = %f", v, endValue, startValue, (v*((Float)endValue-(Float) startValue)));
                        mProperty.set(target, v*((Float) endValue-(Float) startValue));
                    }else {
                        mProperty.set(target, v);
                    }
                    LogUtil.i("object animator value: %f, value is integer: %b, value is float: %b", v, timelineCurve==null, isInteger, isFloat);
                }
            }
        });
    }


    @Override
    public void start() {
        final Object target = getTarget();
        if (mTarget != null && target == null) {
            // We lost the target reference, cancel and clean up. Note: we allow null target if the
            /// target has never been set.
            cancel();
            return;
        }
        LogUtil.i("start animation");
        super.start();
    }

    boolean isInitialized() {
        return mInitialized;
    }

    @Override
    public String toString() {
        String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " + getTarget();
//        if (mValues != null) {
//            for (int i = 0; i < mValues.size(); ++i) {
//                returnVal += "\n    " + mValues.get(i).toString();
//            }
//        }
        return returnVal;
    }
}
